package com.afdor.rws.event.driven.entities;

import com.afdor.rws.core.extension.ExtensionLoader;
import com.afdor.rws.core.strategy.IdGeneratorStrategy;
import com.afdor.rws.core.strategy.TimestampStrategy;
import com.afdor.rws.event.driven.EventChangeManager;
import com.afdor.rws.event.driven.EventHolder;
import com.afdor.rws.event.driven.EventPersistenceManager;
import com.afdor.rws.event.driven.config.EventConfig;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 事件对象
 *
 * @author 悭梵
 * @date Created in 2018-08-03 16:48
 */
@Slf4j
@Setter
@Getter
@ToString
@EqualsAndHashCode
public class Event<T> implements EventHolder, Serializable, Cloneable {
    /**
     * 事件路径分隔符
     */
    public static final String EVENT_PATH_SPLIT = "/";

    /**
     * 事件唯一标识
     */
    @NonNull
    private String id;
    /**
     * 事件类型
     */
    @NonNull
    private Type type;

    /**
     * 事件发送方式
     */
    @NonNull
    private SendType sendType;
    /**
     * 事件执行状态
     */
    @NonNull
    private Status status;
    /**
     * 上级事件标识
     */
    private String parentId;
    /**
     * 当前事件路径
     */
    private String path;
    /**
     * 事件源/队列名称
     *
     * @return
     */
    @NonNull
    protected String eventSourceName;
    /**
     * 事件源
     */
    @NonNull
    protected T source;
    /**
     * 事件执行状态时间
     */
    @NonNull
    private long statusTime;
    /**
     * 事件发生时间
     */
    @NonNull
    private long occurrenceTime;
    /**
     * 重试次数
     * <li>注意该数据未持久化。</li>
     * <li>在各状态的执行次数。即：状态修改时，重置重试次数。</li>
     */
    private int retryCount = 0;

    private Map<String, String> tagMap;

    public Event() {
    }

    /**
     * 派生一个新事件
     *
     * @param parentEvent
     */
    Event(@NonNull Event parentEvent) {
        this();
        this.parentId = parentEvent.getId();
        this.path = parentEvent.getPath() + EVENT_PATH_SPLIT + this.id;
    }

    /**
     * 是否根事件
     *
     * @return
     */
    public boolean isRootEvent() {
        return StringUtils.isBlank(parentId);
    }

    public void setType(@NonNull Type type) {
        this.type = type;
    }

    public final void setStatus(@NonNull Status status) {
        // 用户过滤掉序列化带来的事件状态变更重复出发的问题
        boolean hasEventChange = this.status == null && Objects.equals(status, Status.NEW) || this.status != null;
        if (!Objects.equals(this.status, status)) {
            this.status = status;

            if (hasEventChange) {
                this.statusTime = getTimestamp();
                if (this.occurrenceTime <= 0) {
                    this.occurrenceTime = this.statusTime;
                }
                eventChanged();
            }
        }
    }

    /**
     * 多线程调用安全性问题由调用发实现
     */
    public void addRetry() {
        setRetryCount(this.retryCount + 1);
    }

    public void setRetryCount(int retryCount) {
        if (retryCount > this.retryCount) {
            this.retryCount = retryCount;
            eventChanged();
        }
    }

    @Override
    public Event getEvent() {
        return this;
    }

    /**
     * 初始化
     */
    protected void init() {
        this.id = getEventId();

        this.parentId = null;
        this.path = EVENT_PATH_SPLIT + this.id;
        setStatus(Status.NEW);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * 事件已改变
     */
    private void eventChanged() {
        EventConfig eventConfig = ExtensionLoader.getInstance(EventConfig.class)
                .getDefaultExtension();
        // 获取配置的事件状态管理器
        EventChangeManager eventChangeManager = ExtensionLoader.getInstance(EventChangeManager.class)
                .getExtensionOrDefault(eventConfig.getEventChangeManagerStrategyName());

        EventPersistenceManager eventPersistenceManager = ExtensionLoader.getInstance(EventPersistenceManager.class)
                .getExtensionOrDefault(eventConfig.getEventPersistenceManagerStrategyName());

        if (eventChangeManager == null && eventPersistenceManager == null) {
            return;
        }
        try {
            Event cloneEvent = (Event) this.clone();
            if (eventPersistenceManager != null) {
                eventPersistenceManager.addEvent(cloneEvent);
            }

            if (eventChangeManager != null) {
                eventChangeManager.eventChanged(cloneEvent);
            }
        } catch (CloneNotSupportedException e) {
            log.error("克隆事件对象失败", e);
        }
    }

    private String getEventId() {
        ExtensionLoader<EventConfig> eventConfigExtensionLoader = ExtensionLoader.getInstance(EventConfig.class);
        ExtensionLoader<IdGeneratorStrategy> idGeneratorStrategyExtensionLoader = ExtensionLoader.getInstance(IdGeneratorStrategy.class);
        // 获取配置的Id生成策略
        return String.valueOf(idGeneratorStrategyExtensionLoader.getExtensionOrDefault(eventConfigExtensionLoader.getDefaultExtension().getIdStrategyName()).generate());
    }

    private long getTimestamp() {
        ExtensionLoader<EventConfig> eventConfigExtensionLoader = ExtensionLoader.getInstance(EventConfig.class);
        ExtensionLoader<TimestampStrategy> timestampStrategyExtensionLoader = ExtensionLoader.getInstance(TimestampStrategy.class);
        //  获取配置的时间获取
        return timestampStrategyExtensionLoader.getExtensionOrDefault(eventConfigExtensionLoader.getDefaultExtension().getTimestampStrategyName()).get();
    }

    @ToString
    public static class EventBuilder<T extends Serializable> {
        /**
         * 事件源/队列名称
         *
         * @return
         */
        @NonNull
        private String eventSourceName;
        /**
         * 事件源
         */
        @NonNull
        private T source;
        /**
         * 事件类型
         */
        private Type type;
        /**
         * 上级事件
         */
        private Event parentEvent;

        private Map<String, String> tagMap;

        public EventBuilder() {
        }

        public EventBuilder<T> eventSourceName(@NonNull String eventSourceName) {
            this.eventSourceName = eventSourceName;
            return this;
        }

        /**
         * 添加、更新标签
         *
         * @param key
         * @param value
         * @return
         */
        public EventBuilder<T> addTag(@NonNull String key, String value) {
            if (tagMap == null) {
                tagMap = new HashMap<>();
            }
            this.tagMap.put(key, value);
            return this;
        }

        /**
         * 删除标签
         *
         * @param key
         * @return
         */
        public EventBuilder<T> removeTag(@NonNull String key) {
            if (tagMap == null) {
                return this;
            }
            this.tagMap.remove(key);
            return this;
        }

        public EventBuilder<T> source(@NonNull T source) {
            this.source = source;
            return this;
        }

        /**
         * 生成Type.BROADCAST事件对象
         *
         * @return
         */
        public Event<T> buildBroadcast() {
            this.type = Type.BROADCAST;
            return build();
        }

        /**
         * 生成Type.BROADCAST事件对象
         *
         * @return
         */
        public Event<T> buildSubEvent(@NonNull Event event) {
            this.type = Type.SUB_EVENT;
            this.parentEvent = event;
            return build();
        }

        /**
         * 生成事件对象
         *
         * @return
         */
        public Event<T> build() {
            if (StringUtils.isBlank(eventSourceName)) {
                throw new NullPointerException("事件源/队列名称不能为空");
            }
            Event event = null;
            if (parentEvent == null) {
                Type $Type = this.type == null ? Type.EVENT : this.type;
                if (Objects.equals(Type.AGGREGATION_ROOT, $Type)) {
                    throw new IllegalStateException("请使用AggregationEvent.EventBuilder构建");
                } else {
                    event = new Event();
                    event.setType($Type);
                }
            } else {
                event = new Event(parentEvent);
                if (type == null) {
                    event.setType(Type.SUB_EVENT);
                } else {
                    event.setType(this.type);
                }
            }
            event.source = this.source;
            event.eventSourceName = this.eventSourceName;
            if (!(this.tagMap == null || this.tagMap.isEmpty())) {
                event.tagMap = Collections.unmodifiableMap(this.tagMap);
            }
            event.init();
            return event;
        }
    }

    /**
     * 事件类型
     */
    public enum Type {
        /**
         * 聚合根事件
         */
        AGGREGATION_ROOT("DEFAULT_EVENT_AGGREGATION_ROOT"),
        /**
         * 广播
         */
        BROADCAST("DEFAULT_EVENT_BROADCAST"),
        /**
         * 普通事件消息
         */
        EVENT(null),
        /**
         * 子事件/派生事件消息
         */
        SUB_EVENT(null);

        private String defaultSourceName;

        Type(String defaultSourceName) {
            this.defaultSourceName = defaultSourceName;
        }

        public String getDefaultSourceName() {
            return defaultSourceName;
        }

        @Override
        public String toString() {
            return this.name();
        }
    }

    /**
     * 事件发送方式
     */
    public enum SendType {
        /**
         * 同步发送
         */
        SYNC,
        /**
         * 异步发送
         */
        ASYNC,
        /**
         * 异步发送，并等待完成回调
         */
        ASYNC_CALLBACK,
        /**
         * 异步事务发送
         */
        ASYNC_TRANSACTION,
        /**
         * 异步事务发送，并等待完成回调
         */
        ASYNC_TRANSACTION_CALLBACK
    }

    /**
     * 事件状态枚举
     */
    public enum Status {
        /**
         * 新建(EventBus)
         */
        NEW_EVENT_BUS(0, "新建(EventBus)"),
        /**
         * 新建(处理方)
         */
        NEW(1, "新建(处理方)"),
        /**
         * 已回滚(EventBus)
         */
        ROLLBACK_EVENT_BUS(3, "已回滚(EventBus)"),
        /**
         * 已回滚(处理方)
         */
        ROLLBACK(4, "已回滚(处理方)"),
        /**
         * 已加入事物(EventBus)
         */
        JOIN_TRANSACTION_EVENT_BUS(5, "已加入事物(EventBus)"),
        /**
         * 执行中(处理方)
         */
        EXECUTING(10, "执行中(处理方)"),
        /**
         * 执行中(EventBus)
         */
        EXECUTING_EVENT_BUS(11, "执行中(EventBus)"),
        /**
         * 待提交(处理方)
         */
        WAITING(20, "待提交(处理方)"),
        /**
         * 待提交(EventBus)
         */
        WAITING_EVENT_BUS(21, "待提交(EventBus)"),
        /**
         * 已提交(处理方)
         */
        COMMITTED(30, "已提交(处理方)"),
        /**
         * 已提交(EventBus)
         */
        COMMITTED_EVENT_BUS(31, "已提交(EventBus)"),
        /**
         * 已回滚(处理方)
         */
        ROLLEDBACK(40, "已回滚(处理方)"),
        /**
         * 已回滚(EventBus)
         */
        ROLLEDBACK_EVENT_BUS(41, "已回滚(EventBus)"),
        /**
         * 已完成(处理方)
         */
        COMPLETED(50, "已完成(处理方)"),
        /**
         * 已完成(EventBus)
         */
        COMPLETED_EVENT_BUS(51, "已完成(EventBus)"),
        /**
         * 执行失败(处理方)
         */
        FAILURED(60, "执行失败(处理方)"),
        /**
         * 执行失败(EventBus)
         */
        FAILURED_EVENT_BUS(61, "执行失败(EventBus)"),
        /**
         * 已终止，重试到最大次数(处理方)
         */
        TERMINATED(70, "已终止(处理方)"),
        /**
         * 已终止，重试到最大次数(EventBus)
         */
        TERMINATED_EVENT_BUS(71, "已终止(EventBus)"),
        /**
         * 放弃执行(处理方)
         */
        ABANDONED(80, "放弃执行(处理方)"),
        /**
         * 放弃执行(EventBus)
         */
        ABANDONED_EVENT_BUS(81, "放弃执行(EventBus)"),
        ;

        private final int status;
        private final String message;

        Status(int status, String message) {
            this.status = status;
            this.message = message;
        }

        public int getStatus() {
            return status;
        }

        public String getMessage() {
            return message;
        }

        public static Status valueOf(int status) {
            Status[] values = values();
            for (int i = 0, length = values.length; i < length; i++) {
                if (values[i].getStatus() == status) {
                    return values[i];
                }
            }
            return null;
        }
    }

}
